#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>

#include "cir_queue.h"

void init_cir_queue(cir_queue *q,int size)
{  
  pthread_mutexattr_t mutexattr;  
  pthread_mutexattr_init(&mutexattr);   
  pthread_mutexattr_setpshared(&mutexattr,PTHREAD_PROCESS_SHARED);  
  
  pthread_condattr_t condattr;  
  pthread_condattr_init(&condattr);   
  pthread_condattr_setpshared(&condattr,PTHREAD_PROCESS_SHARED);  
  
  q->m=malloc(sizeof(pthread_mutex_t));
  q->c=malloc(sizeof(pthread_cond_t));
  pthread_mutex_init(q->m, &mutexattr);
  pthread_cond_init(q->c, &condattr);

  q->size=size;
  q->closed=0;
  q->data=malloc(size*sizeof(char *));
  memset(q->data, 0, size*sizeof(char *));

  q->head = q->tail = 0;
}

int is_empty_cir_queue(cir_queue *q)
{
  return q->head == q->tail;
}

int is_full_cir_queue(cir_queue *q)
{
  return ((q->tail + 1) % q->size) == q->head;
}

int is_closed_queue(cir_queue *q)
{
  return q->closed;
}

void push_cir_queue(cir_queue *q, char *x)
{
  if (q->closed) return; 
  (void) pthread_mutex_lock(q->m);
  while (is_full_cir_queue(q) && !q->closed)
  {
//    printf("queue full,wait...\n");
    (void) pthread_cond_wait(q->c, q->m);  
  } 
  if (!q->closed) 
  {
    q->data[q->tail] = x;
    q->tail = (q->tail+1) % q->size;
  }
  (void) pthread_mutex_unlock(q->m); 
  (void) pthread_cond_signal(q->c); 
}

char *pop_cir_queue(cir_queue *q)
{
  if (q->closed) return NULL; 

  (void) pthread_mutex_lock(q->m);
  while (is_empty_cir_queue(q) && !q->closed)
  {
//    printf("queue empty,wait...\n");
    (void) pthread_cond_wait(q->c, q->m);  
  }
  char *temp=NULL;
  if (!q->closed) 
  {
    temp= q->data[q->head];
    q->data[q->head] = NULL;
    q->head = (q->head+1) % q->size;
  }
  (void) pthread_mutex_unlock(q->m);
  (void) pthread_cond_signal(q->c); 

  return temp;
}

void close_cir_queue(cir_queue *q)
{ 
  q->closed=1;
  (void) pthread_cond_signal(q->c); 
  return;    
}

void destroy_cir_queue(cir_queue *q)
{ 
  pthread_mutex_destroy(q->m);
  pthread_cond_destroy(q->c);

  free(q->m);
  free(q->c);
  free(q->data);
  return;    
}

int  get_count_queue(cir_queue *q)
{
  int count=q->tail - q->head;
  if (count<0)
    count += q->size;
  return count;
}